#include "tbcore/base/unique_id.hpp"

#include <boost/atomic/atomic.hpp>
#include <boost/functional/hash.hpp>
#include <boost/scoped_ptr.hpp>

#include "tbcore/util/random.hpp"
#include "tbcore/util/hex.hpp"

#include "tbcore/base/global_initializer.hpp"
#include "tbcore/base/logging.hpp"

TB_NAMESPACE_BEGIN

namespace {

const uint32 kTimestampOffset = 0;
const uint32 kInstanceUniqueOffset = kTimestampOffset + UniqueId::kTimestampSize;
const uint32 kIncrementOffset = kInstanceUniqueOffset + UniqueId::kInstanceUniqueSize;
const uchar kNullObjectIdData[UniqueId::kUniqueIdSize] = { 0 };

struct ObjectIdInstanceUnique {
	uchar data_[UniqueId::kInstanceUniqueSize];
	static ObjectIdInstanceUnique Generate(SecureRandom& gen) {
		ObjectIdInstanceUnique u;
		int64 r = gen.nextInt64();
		std::memcpy(u.data_, &r, UniqueId::kInstanceUniqueSize);
		return u;
	}
};

std::unique_ptr<boost::atomic_int> gObjectIdCounter;
ObjectIdInstanceUnique gObjectIdInstanceUnique;

struct ObjectIdIncrement {
	uchar data_[UniqueId::kIncrementSize];
	static ObjectIdIncrement Next() {
		ObjectIdIncrement i;
		int32 c = gObjectIdCounter->fetch_add(1);
		std::memcpy(i.data_, &c, UniqueId::kIncrementSize);
		return i;
	}
};
 
TB_INITIALIZER_GENERAL(ObjectIdInitializer, TB_NO_PREREQUISITES, TB_DEFAULT_DEPENDS)
	(const InitializerContext& context) {
	boost::scoped_ptr<SecureRandom> gen(SecureRandom::create());
	gObjectIdCounter.reset(new boost::atomic_int((int)gen->nextInt64()));
	gObjectIdInstanceUnique = ObjectIdInstanceUnique::Generate(*gen);
	return Result();
}

struct ObjectIdHelper {
	static void SetTimestamp(uchar (&d)[UniqueId::kUniqueIdSize], int32 i) {
		std::memcpy(d + kTimestampOffset, &i, UniqueId::kTimestampSize);
	}

	static void SetInstanceUnique(uchar (&d)[UniqueId::kUniqueIdSize], ObjectIdInstanceUnique& u) {
		std::memcpy(d + kInstanceUniqueOffset, u.data_, UniqueId::kInstanceUniqueSize);
	}

	static void SetIncrement(uchar (&d)[UniqueId::kUniqueIdSize], ObjectIdIncrement& i) {
		std::memcpy(d + kIncrementOffset, i.data_, UniqueId::kIncrementSize);
	}
};

} //namespace 

size_t UniqueId::Hasher::operator()(const UniqueId& oid) const {
	size_t seed = 0;
	oid.HashCombine(seed);
	return seed;
}

UniqueId::UniqueId() {
	_STD memcpy(data_, kNullObjectIdData, kUniqueIdSize);
}

UniqueId::UniqueId(const std::string& idStr) {
	if (idStr.size() == 24) {
		const char *p = idStr.c_str();
		for (std::size_t i = 0; i < kUniqueIdSize; i++) {
			data_[i] = fromHex(p);
			p += 2;
		}
	} else {
		LERROR() << "object id string size is not rqual 24!";
	}
}

UniqueId::UniqueId(const uint8 (&arr)[kUniqueIdSize]) {
	std::memcpy(data_, arr, kUniqueIdSize);
}

UniqueId::UniqueId(const UniqueId& rhs) {
	std::memcpy(data_, rhs.data_, kUniqueIdSize);
}

UniqueId& UniqueId::operator=(const UniqueId& rhs) {
	if (this != &rhs) {
		std::memcpy(data_, rhs.data_, kUniqueIdSize);
	}
	return *this;
}

UniqueId::~UniqueId() {

}

int UniqueId::Compare(const UniqueId& rhs) const {
	return std::memcmp(data_, rhs.data_, kUniqueIdSize);
}

std::string UniqueId::ToString() const {
	return toHexLower(data_, kUniqueIdSize);
}

void UniqueId::HashCombine(std::size_t seed) const {
	uint32 v;
	for (int i = 0; i < kUniqueIdSize; i += sizeof(uint32)) {
		_STD memcpy(&v, data_ + i, sizeof(uint32));
		boost::hash_combine(seed, v);
	}
}

UniqueId UniqueId::GenUID() {
	UniqueId oid;
	ObjectIdHelper::SetTimestamp(oid.data_, (int32)time(0));
	ObjectIdHelper::SetInstanceUnique(oid.data_, gObjectIdInstanceUnique);
	ObjectIdHelper::SetIncrement(oid.data_, ObjectIdIncrement::Next());
	return oid;
}

void UniqueId::Fork() {
	boost::scoped_ptr<SecureRandom> gen(SecureRandom::create());
	gObjectIdCounter.reset(new boost::atomic_int((int)gen->nextInt64()));
	gObjectIdInstanceUnique = ObjectIdInstanceUnique::Generate(*gen);
}

bool UniqueId::IsNull() const {
	return _STD memcmp(data_, kNullObjectIdData, kUniqueIdSize) == 0;
}

const char* UniqueId::ConstData() const {
	return (char*)&data_;
}

char* UniqueId::Data() const {
  return (char*)&data_;
}

TB_NAMESPACE_END